package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.example.constant.NumConstant;
import com.example.constant.RedisConstance;
import com.example.dto.OrderDTO;
import com.example.dto.OrderItemDTO;
import com.example.dto.TradeDTO;
import com.example.entity.*;
import com.example.mapper.*;
import com.example.service.SkillGoodsService;
import com.example.util.RedisUtil;
import com.example.vo.IdHolder;
import com.example.vo.Result;
import com.example.vo.ResultCode;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.example.constant.RedisConstance.USER_ORDER_TOKEN_PREFIX;

@Service
public class SkillGoodsServiceImpl implements SkillGoodsService {
    @Autowired
    private SkillGoodsMapper skillGoodsMapper;
    private final Random random = new Random();
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private SkillOrderMapper skillOrderMapper;
    @Resource
    private SkuMapper skuMapper;
    @Resource
    private SpuMapper spuMapper;
    @Resource
    private StoreMapper storeMapper;
    @Resource
    private RedisUtil redisUtil;
    //开始秒杀的时间点
    public static final int[] beginHours = {10, 11,12, 14,15,18, 20,21, 22};
    //private final int[] beginHours = {16,18,20,21};

    @Override
    public Result skillGoods(Long skillId) {
        int currHour = LocalDateTime.now().getHour();
        int hour = Arrays.stream(beginHours).filter(h -> h >= currHour).min().orElse(-1);
        //System.out.println("hour = " + hour);
        if(hour == -1){
            return Result.error("当前没有秒杀活动");
        }else if(hour != currHour){
            return Result.error("秒杀还未开始");
        }
        //模拟生成userId
        //long userId = random.nextInt(100) + 1;
        Long userId = IdHolder.getUserId();

        String qtKey = RedisConstance.SKILL_GOODS_PREFIX + skillId;
        String stock = stringRedisTemplate.opsForValue().get(qtKey);
        if (stock == null) {
            return Result.error("秒杀商品不存在");
        }
        String luaScript = """
                local userid=KEYS[1];
                local prodid=KEYS[2];
                local qtkey="i-shop:skill:goods:"..prodid;
                local usersKey="i-shop:skill:userList:"..prodid;
                local userExists=redis.call("sismember",usersKey,userid);
                if tonumber(userExists)==1 then
                  return 2;
                end
                local num= redis.call("get" ,qtkey);
                if tonumber(num)<=0 then
                  return 0;
                else
                  redis.call("decr",qtkey);
                  redis.call("sadd",usersKey,userid);
                end
                return 1;                         
                """;
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        List<String> args = Arrays.asList(Long.toString(userId), skillId.toString());

        Long redisResult = stringRedisTemplate.execute(redisScript, args);
        // 进行判断返回值，根据不同返回值构造响应结果
        if (redisResult == null) {
            return Result.error("该商品不是秒杀商品");
        }
        Result result = null;
        if (redisResult == 2) {
            System.out.println("用户" + userId + "已经秒杀过了");
            result = Result.error("你已经秒杀过了");
        } else if (redisResult == -1) {
            System.out.println("秒杀商品不存在");
            result = Result.error("秒杀商品不存在");
        } else if (redisResult == 0) {
            System.out.println("商品库存不足");
            result = Result.error("商品库存不足");
        } else if (redisResult == 1) {
            System.out.println("用户" + userId + "秒杀成功");
            //创建秒杀成功订单
            SkillGoods skillGoods = skillGoodsMapper.selectById(skillId);
            //封装秒杀订单信息
            SkillOrder skillOrder = new SkillOrder();
            skillOrder.setSkillId(skillId);
            skillOrder.setUserId(userId);
            skillOrder.setMoney(skillGoods.getSkillPrice());
            skillOrder.setSkillTime(LocalDateTime.now());
            skillOrder.setStatus(NumConstant.ORDER_STATUS_UNPAY);
            skillOrder.setCreateTime(LocalDateTime.now());
            skillOrder.setUpdateTime(LocalDateTime.now());
            skillOrderMapper.insert(skillOrder);

            String orderKey = getTradeDto(skillId);
            if(orderKey != null){
                result = Result.ok(orderKey);
            }else{
                result = Result.error(ResultCode.GOOD_NOT_SELECTED);
            }
        }
        return result;
    }

    //在redis中，增加秒杀商品信息
    @Override
    public void insertSkillGoods(){
        int currHour = LocalDateTime.now().getHour();
        int hour = Arrays.stream(beginHours).filter(h -> h >= currHour).min().orElse(-1);
        System.out.println("hour = " + hour);
        if(hour != -1){
            LocalDate date = LocalDate.now();
            LocalTime time = LocalTime.of(hour, 0, 0);
            LocalDateTime beginTime = LocalDateTime.of(date, time);
            List<SkillGoods> willBeginList = skillGoodsMapper.getWillBeginSkillGoods(beginTime);
            String key = RedisConstance.SKILL_GOODS_LIST_PREFIX+hour;
            willBeginList.forEach(skillGoods -> {
                //保存商品信息
                String goodsStr = JSON.toJSONString(skillGoods);
                stringRedisTemplate.opsForList().rightPush(key,goodsStr);
                int minute = time.getMinute();
                //stringRedisTemplate.expire(key,(60-minute)+60,TimeUnit.MINUTES);
                //保存商品库存信息
                String stockKey = RedisConstance.SKILL_GOODS_PREFIX + skillGoods.getId();
                stringRedisTemplate.opsForValue().set(stockKey,skillGoods.getSkillStock().toString(),60 * 60 + 10, TimeUnit.SECONDS);
            });
        }
    }

    //将秒杀订单转换为TradeDto
    public String getTradeDto(Long skillId){
        SkillGoods skillGoods = skillGoodsMapper.selectById(skillId);
        BigDecimal skillPrice = skillGoods.getSkillPrice().setScale(2, RoundingMode.HALF_UP);
        Long skuId = skillGoods.getSkuId();
        OrderDTO orderDTO = new OrderDTO();

        Sku sku = skuMapper.selectById(skuId);
        Store store = storeMapper.selectById(sku.getStoreId());

        //System.out.println("orderDTO = " + orderDTO);

        //订单项信息
        OrderItemDTO orderItemDTO = new OrderItemDTO();
        BeanUtils.copyProperties(sku,orderItemDTO);
        orderItemDTO.setSkuQuantity(1);
        orderItemDTO.setSkuPic(sku.getDefaultImage());
        orderItemDTO.setId(IdWorker.getId());
        orderItemDTO.setSkuPrice(skillPrice);
        orderItemDTO.setSkuName(sku.getName());
        orderItemDTO.setSkuId(skillGoods.getSkuId());
        Spu spu = spuMapper.selectById(sku.getSpuId());
        orderItemDTO.setSpuName(spu.getName());
        //订单信息
        orderDTO.setItems(List.of(orderItemDTO));
        orderDTO.setPayAmount(skillPrice);
        orderDTO.setTotalAmount(skillPrice);
        orderDTO.setStoreId(store.getId());
        orderDTO.setStoreName(store.getName());
        orderDTO.setNote("订单备注");
        orderDTO.setPromotionAmount(BigDecimal.ZERO);
        orderDTO.setCouponAmount(BigDecimal.ZERO);
        orderDTO.setDiscountAmount(BigDecimal.ZERO);
        orderDTO.setFreightAmount(BigDecimal.ZERO);

        // 防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        Long userId = IdHolder.getUserId();
        stringRedisTemplate.opsForValue().set(USER_ORDER_TOKEN_PREFIX + ":" + userId, token, 30, TimeUnit.MINUTES);
        TradeDTO tradeDTO = new TradeDTO();
        tradeDTO.setOrders(List.of(orderDTO));
        tradeDTO.setOrderToken(token);
        tradeDTO.setFlowPrice(skillPrice);
        tradeDTO.setFreightPrice(BigDecimal.ZERO);
        tradeDTO.setDiscountPrice(BigDecimal.ZERO);
        tradeDTO.setGoodsPrice(skillPrice);
        tradeDTO.setType(2);
        //System.out.println("tradeDTO = " + tradeDTO);

        //将trade保存到redis
        String orderKey = null;
        if ( tradeDTO.getOrders() != null && !tradeDTO.getOrders().isEmpty()) {
            // 将订单数据存储到 Redis
            orderKey = "order:" + UUID.randomUUID().toString();
            redisUtil.set(orderKey, tradeDTO, 30*60); // 存储 30 分钟
            return orderKey;
        }
        return orderKey;
    }

    @Override
    public List<SkillGoods> getSkillGoods() {
        int currHour = LocalDateTime.now().getHour();
        int hour = Arrays.stream(beginHours).filter(h -> h >= currHour).min().orElse(-1);
        if(hour == -1){
            return new ArrayList<>();
        }
        String goodsKey = RedisConstance.SKILL_GOODS_LIST_PREFIX + hour;
        List<String> strings = stringRedisTemplate.opsForList().range(goodsKey, 0, -1);
        ArrayList<SkillGoods> skillGoodsList = new ArrayList<>();
        if(strings != null && !strings.isEmpty()){
            strings.forEach(s->{
                SkillGoods skillGoods = JSON.parseObject(s, SkillGoods.class);
                //获取已销售的数量
                String stockKey = RedisConstance.SKILL_GOODS_PREFIX + skillGoods.getId();
                String sellCountStr = stringRedisTemplate.opsForValue().get(stockKey);
                int remind = 0;
                if(sellCountStr != null){
                    remind = Integer.parseInt(sellCountStr);
                }
                skillGoods.setSellCount(skillGoods.getSkillStock()-remind);
                skillGoodsList.add(skillGoods);
            });
        }
        return skillGoodsList;
    }

    @Override
    public List<Map<String, Object>> getSkillTimeList() {
        int[] timeArr = getTimeArr();
        ArrayList<Map<String, Object>> maps = new ArrayList<>();
        for (int hour : timeArr) {
            HashMap<String, Object> map = new HashMap<>();
            long beginStamp = getHourStamp(hour);
            long endStamp = getHourStamp(hour + 1);
            map.put("beginStamp",beginStamp);
            map.put("endStamp",endStamp);
            map.put("time",hour+":00");
            maps.add(map);
        }
        return maps;
    }

    //获取时间戳
    private long getHourStamp(int hour) {
        LocalDateTime dateTime = LocalDateTime.now()
                .withHour(hour)
                .withMinute(0)
                .withSecond(0)
                .withNano(0);
        return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    private int[] getTimeArr() {
        LocalDateTime now = LocalDateTime.now();
        int currentHour = now.getHour();
        int beforeHour = Arrays.stream(beginHours).filter(h -> h <= currentHour).max().orElse(-1);
        int[] sequence;
        // 没有比这更早的时间了，返回前三个时间点
        if (beforeHour == -1) {
            sequence = Arrays.copyOfRange(beginHours, 0, 3);
        } else {
            sequence = Arrays.stream(beginHours).filter(h -> h >= beforeHour).limit(3).toArray();
            //如果是最后两个时间点，返回最后三个时间段
            if (sequence.length < 3) {
                sequence = Arrays.copyOfRange(beginHours, beginHours.length - 3, beginHours.length);
            }
        }
        return sequence;
    }
}